Skip to content

yield 表达式 ​

本节目标:理解“yield 表达式”的核心思路,并能把它用于实际开发或面试表达。

说明:原 OneNote 中有图片引用,但图片未包含在导出目录中;本页保留了可用的文字与代码内容。 由于 Generator 函数返回的遍历器对象,只有调用next方法才会遍历下一个内部状态,所以其实提供了一种可以暂停执行的函数。yield表达式就是暂停标志。

遍历器对象的next方法的运行逻辑 (1)遇到yield表达式,会执行yield后面的语句,并将紧跟在yield后面的那个表达式的值,作为返回的对象的value属性值,然后函数停止执行; (2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式。 (3)如果没有再遇到新的yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值。 (4)如果该函数没有return语句,则返回的对象的value属性值为undefined。

需要注意的是,yield表达式后面的表达式,只有当调用next方法、内部指针指向该语句时才会执行,因此等于为 JavaScript 提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。
function* gen() {  yield  123 + 456;}
上面代码中,yield后面的表达式123 + 456,不会立即求值,只会在next方法将指针移到这一句时,才会求值。
**y****ield****与****return**
相同处:返回紧跟在语句后面的那个表达式的值。
区别:

在于每次遇到yield,函数暂停执行,下一次再从该位置继续向后执行;

而return语句不具备位置记忆的功能,return后函数就退出了。
只能执行一次(或者说一个)return语句。
可以执行多次(或者说多个)yield表达式。
**不用****yield****表达式**
这时就变成了一个单纯的暂缓执行函数。
function* f() {  console.log('执行了!')}
var generator = f();
setTimeout(function () {  generator.next()}, 2000);
只有调用next方法时,函数f才会执行。
**yield****使用**
yield表达式只能用在 Generator 函数里面,用在其他地方都会报错。
(function (){  yield 1;})()// SyntaxError: Unexpected number
在循环中使用yield,如:
var arr = [1, [[2, 3], 4], [5, 6]];
var flat = function* (a) {  a.forEach(function (item) {    if (typeof item !== 'number') {      yield* flat(item);    } else {      yield item;    }  });};
for (var f of flat(arr)){  console.log(f);}
上面代码也会产生句法错误,因为forEach方法的参数是一个普通函数,但是在里面使用了yield表达式(这个函数里面还使用了yield*表达式,详细介绍见后文)。一种修改方法是改用for循环。
var arr = [1, [[2, 3], 4], [5, 6]];
var flat = function* (a) {  var length = a.length;  for (var i = 0; i < length; i++) {    var item = a[i];    if (typeof item !== 'number') {      yield* flat(item);    } else {      yield item;    }  }};
for (var f of flat(arr)) {  console.log(f);}// 1, 2, 3, 4, 5, 6
另外,yield表达式如果用在另一个表达式之中,必须放在圆括号里面。
function* demo() {  console.log('Hello' + yield); // SyntaxError  console.log('Hello' + yield 123); // SyntaxError
  console.log('Hello' + (yield)); // OK  console.log('Hello' + (yield 123)); // OK}
yield表达式用作函数参数或放在赋值表达式的右边,可以不加括号。
function* demo() {  foo(yield 'a', yield 'b'); // OK  let input = yield; // OK}
**异步的迭代,只能用递归**

await后面跟执行器返回的是一个timer**,没有实际意义;** await****后面如果跟普通值 **,会被Promise.resolve包装成****promise,**放到任务队列之中